var CLIENT_VERSION = '1.0';

function Room(boxes, door, background) {
    var self = this;
    this.boxes = boxes;
    this.door = door;
    this.background = background;
}

function World(seed, canvas) {
    var self = this;
    this.rooms = {};
    this.currentLevel = 0;
    this.currentScore = 0;
    this.deathTime = 500;

    this.getRoom = function(level) {
        if(!self.rooms[level]) {
            self.rooms[level] = self.createRoom(level, canvas);
        }
        return self.rooms[level];
    };

    this.resetRooms = function() {
        self.rooms = {};
    };

    this.createRoom = function(idx, canvas) {
        Math.seedrandom(idx + seed);
        var entities = [];
        var boxes = [];

        var nbrOfBoxes = parseInt(2 + ((idx * 1.3 + 1) * Math.random()));
        if(idx > 0) {
            entities.push(this.getRoom(idx-1).door);
        }
        function collides(x, y, width, height) {
            for (var entityIdx in entities) {
                var entity = entities[entityIdx];
                if (!(x + width < entity.x || x > entity.x + entity.width || y + height < entity.y || y > entity.y + entity.height)) {
                    return true;
                }
            }
            return false;
        }

        var x, y;
        var boxWidth = 35;
        var boxHeight = 35;
        for (var i = 0; i < nbrOfBoxes; i++) {
            var nbrOfClicks = parseInt(1 + (Math.random() * (2.4 * (idx + 1))));
            do {
                x = parseInt(Math.random() * (canvas.width - boxWidth));
                y = parseInt(Math.random() * (canvas.height - boxHeight));
            } while (collides(x, y, boxWidth, boxHeight))

            var box = new Box(nbrOfClicks);
            box.EntityBase_init(x, y, boxWidth, boxHeight);

            boxes.push(box)
            entities.push(box);
        }

        var doorWidth = boxWidth;
        var doorHeight = boxHeight;
        do {
            x = parseInt(Math.random() * (canvas.width - doorWidth));
            y = parseInt(Math.random() * (canvas.height - doorHeight));
        } while (collides(x, y, doorWidth, doorHeight))

        var door = new Door();
        door.EntityBase_init(x, y, doorWidth, doorHeight);

        entities.push(door);
        return new Room(boxes, door, new BackgroundEntity(canvas));
    }
}

function RecordedRound() {
    this.mouseStates = [];
}

function CursorN(options, canvas) {
    var self = this;
    this.options = options;
    this.canvas = canvas;
    this.context = canvas.getContext("2d");

    //temporary drawing buffer
    this.buffer = document.createElement('canvas');
    this.buffer.width = this.canvas.width;
    this.buffer.height = this.canvas.height;
    this.bufferContext = this.buffer.getContext("2d");

    this.mouseInputDevice = new MouseInputDevice(canvas);
    //this.log('Welcome to Cursor*N' + CLIENT_VERSION);
}

CursorN.prototype.start_gameloop = function(initial_tick) {
    var self = this;
    var currentTime = 0;
    this.world = new World(0, self.canvas);

    var gameloop = new GameLoop(initial_tick);
    var lastBtn1 = false;
    var history = [];
    var currentRecording = new RecordedRound();
    history.push(currentRecording);

    // Is called on each game tick.
    gameloop.ontick = function(t, dt) {

        if (currentTime >= self.world.deathTime) {
            currentRecording = new RecordedRound();
            currentTime = 0;
            self.world.currentLevel = 0;
            self.world.resetRooms();
            history.push(currentRecording);
        }

        var activeRoom = self.world.getRoom(self.world.currentLevel);
        activeRoom.background.ontick(t, dt);

        var mid = self.mouseInputDevice;
        currentRecording.mouseStates.push({x: mid.x, y: mid.y, btn1: mid.btn1, level: self.world.currentLevel});

        for (var historyIdx in history) {
            var recording = history[historyIdx];
            var mouseState = recording.mouseStates[currentTime];
            var currentRoom = self.world.getRoom(mouseState.level);

            //Find a click
            var isClick = false;
            if (currentTime != 0) {
                var previouseMouseState = recording.mouseStates[currentTime-1];
                if (!previouseMouseState.btn1 && mouseState.btn1) {
                    isClick = true;
                }
            }

            if (isClick) {
                for (var boxIdx in currentRoom.boxes) {
                    var box = currentRoom.boxes[boxIdx];
                    if (box.collidesWith(mouseState.x, mouseState.y)) {
                        box.clicked();
                        self.world.currentScore++;
                        if ((box.life - box.clickCount) <= 0) {
                            boxIdx = currentRoom.boxes.indexOf(box);
                            currentRoom.boxes.splice(boxIdx, 1);
                            if (currentRoom.boxes.length <= 0) {
                                currentRoom.door.setOpen(true);
                            }
                        }
                    }
                }

                if(historyIdx == history.length-1) {
                    if (currentRoom.door.collidesWith(mouseState.x, mouseState.y)) {
                        if (currentRoom.door.isOpen) {
                            self.world.currentLevel++;
                        }
                    }
                }
            }
        }
        currentTime++;

        //self.process_user_input(t, dt);
        //self.world.update(t, dt);
        //console.info(dt + ", " + self.mouseInputDevice.x + ", " + self.mouseInputDevice.y + ', btn1: ' + self.mouseInputDevice.btn1);
    };

    // Is called when loop is about to start over.
    gameloop.ondone = function(t, dt, alpha) {
        /**

         var destX = 69;
         var destY = 50;

         var imageData = context.getImageData(0, 0, gameCanvas.width, gameCanvas.height);
         var data = imageData.data;

         for (var i = 0; i < data.length; i += 4) {
         data[i] = 128; // red
         data[i + 1] = 128; // green
         data[i + 2] = 128; // blue

         data[i + 3] = 255; // alpha
         }

         // overwrite original image
         */
        self.bufferContext.fillStyle = 'rgb(70, 70, 70)';
        self.bufferContext.fillRect(0, 0, self.canvas.width, self.canvas.height);



        var currentRoom = self.world.getRoom(self.world.currentLevel);

        //currentRoom.background.draw(self.bufferContext);

        for (boxIdx in currentRoom.boxes) {
            var box = currentRoom.boxes[boxIdx];
            box.draw(self.bufferContext);
        }

        currentRoom.door.draw(self.bufferContext);

        if(self.world.currentLevel != 0) {
            var previousRoom = self.world.getRoom(self.world.currentLevel-1);
            previousRoom.door.draw(self.bufferContext, true);

        }


        for (var historyIdx in history) {
            var recording = history[historyIdx];
            var mouseState = recording.mouseStates[currentTime-1];
            self.bufferContext.fillStyle = 'rgba(255,0,0, ' + ((historyIdx == history.length - 1) ? '1' : '0.5') + ')';
            if(mouseState.level == self.world.currentLevel) {
                self.bufferContext.fillRect(mouseState.x, mouseState.y, 10, mouseState.btn1 ? 10 * 2 : 10);
            }
        }

        self.bufferContext.textBaseline = "top";
        self.bufferContext.fillStyle = 'rgb(64,128,64)';
        self.bufferContext.fillText("Level: " + self.world.currentLevel, 0, 0);
        self.bufferContext.fillText("Score: " + self.world.currentScore, 0, 20);
        self.bufferContext.fillText("Time: " + (self.world.deathTime - currentTime), 0, 40);

        //Flush to visible canvas
        self.context.drawImage(self.buffer, 0, 0);
    };

    gameloop.start();
    self.gameloop = gameloop;

    return gameloop;
};
